/**
 * 精灵类2.0
 * 加入动画计时器、矢量类
 */
if(window) {
if(!window.Sprite) {

window.Sprite = (function () {
    /**
     * 向量类的构造函数
     * @param {Number} x x轴属性
     * @param {Number} y y轴属性
     */
    function Vector(x, y) {
        this.x = x || 0;
        this.y = y || 0;
    }

    /* ====================静态方法==================== */
    /**
     * 计算两个向量之和，并返回新向量 
     * @param {Vector} v1 向量1
     * @param {Vector} v2 向量2
     * @return {Vector} 
     * 几何意义：一个位于原始向量夹角之间的，大小为分量之和的新向量
     */
    Vector.add = function(v1, v2) {
        return new Vector(v1.x + v2.x, v1.y + v2.y);
    }

    /**
     * 计算两个向量之差，并返回新向量 （）
     * @param {Vector} v1 向量1
     * @param {Vector} v2 向量2
     * @return {Vector} 差向量
     * 几何意义：一个从原点出发，平行于原始向量终点连线的，大小为分量之差的新向量
     */
    Vector.sub = function(v1, v2) {
        return new Vector(v1.x - v2.x, v1.y - v2.y);
    }

    /**
     * 计算两个向量的点积结果并返回
     * @param {Vector} v1 向量1
     * @param {Vector} v2 向量2
     * @return {Number} 点积结果
     * 几何意义：点积是标量，其重要意义在于它与0的大小关系：
     *      > 0：说明两个向量方向大概一致
     *      < 0：说明两个向量方向大概相反
     *      = 0：说明两个向量发现刚好相反、且大小相同
     */
    Vector.DotProduct = function(v1, v2) {
        return v1.x * v2.x + v1.y * v2.y;
    }

    /**
     * 克隆指定向量并缩放后返回
     * @param {Vector} v 要缩放的向量
     * @param {Number} s 缩放系数
     */
    Vector.scale = function (v, s) {
        return v.clone().scale(s);
    }

    /**
     * 横纵坐标值在[-1, 1)范围内随机取值的新矢量对象
     * @return {Vector} 随机横纵坐标的新矢量实例
     */
    Vector.random = function () {
        return new Vector(
            Math.random() * 2 - 1,
            Math.random() * 2 - 1
        )
    }


    /* ====================原型对象==================== */
    Vector.prototype = {
        /**
         * 给入一个向量实例，或者x、y分量值，设置对应分量，并返回实例本身
         * @param {Vector, Number} vx 可以是Vector类的实例，也可以是一个数值，用于设置当前实例的x轴分量
         * @param {Number} y 如果x是Number类型，则y有意义，且为Number，设置当前实例的y轴分量
         * @return {Vector} this 返回当前实例
         * 几何意义：已赋值的形式，改变了主调向量的 方向 和 模;
         */
        set: function (vx, y) {
            if(typeof vx === 'object') {
                y = vx.y;
                vx = vx.x;
            }
            this.x = vx || 0;
            this.y = y || 0;
            return this;
        },

        /**
         * 为当前实例加上一个向量
         * @param {Vector} v 作为加数的Vector实例
         * @return {Vector} 当前实例
         * 几何意义：以加法的形式，改变了主调向量的 方向 和 模；
         */
        add: function (v) {
            this.x += v.x;
            this.y += v.y;
            return this;
        },

        /**
         * 当前实例减去一个向量
         * @param {Vector} v 作为减数的Vector实例
         * @return {Vector} 当前实例
         * 几何意义：以减法的形式改变了主调向量的 方向 和 模；
         */
        sub: function (v) {
            this.x -= v.x;
            this.y -= v.y;
            return this;
        },

        /**
         * 当前向量分量数值缩放
         * @param {Number} s 缩放系数
         * @return {Vector} 当前向量
         * 几何意义：通过缩放分量，改变主调向量的 模，方向不变
         */
        scale: function (sx, sy) {
            if(sy) {
                this.x *= sx;
                this.y *= sy;
            } else {
                this.x *= sx;
                this.y *= sx;
            }
            return this;
        },

        /**
         * 计算当前向量的模
         * @return {Number} 当前向量的模
         * 几何意义：向量的模（模：向量的大小）
         * 物理意义：速度、位移等矢量概念的标量表示
         */
        mode: function () {
            return Math.sqrt(this.x * this.x + this.y * this.y);
        },

        /**
         * 分量的平方和
         * @return {Number} 当前向量两个分量的平方和
         * 意义：很多时候会数学计算中会用到这个数值，我们抛出一个方法可以直接获取
         */
        sumOfSq: function () {
            return this.x * this.x + this.y * this.y;
        },

        /**
         * @param {Vector} v 被减向量
         * @return {Number} 两向量的差向量的模
         * 几何意义：是两个坐标点的距离
         */
        modeTo: function (v) {
            var dx = v.x - this.x,
                dy = v.y - this.y;
            return Math.sqrt(dx * dx + dy * dy);
        },

        /**
         * 和指定向量的差向量的分量平方和
         * @param {Vector} v 被减向量
         * @return {Number} 差向量的分量的平方和
         */
        sumOfModeToSq: function (v) {
            var dx = v.x - this.x,
                dy = v.y - this.y;
            return dx * dx + dy * dy;
        },

        /**
         * 单位向量化
         * @return {Object} result 结构：{x: cos(a), y:sin(a)}，其中a为向量与水平轴夹角度数
         */
        normalize: function () {
            var m = Math.sqrt(this.x * this.x + this.y * this.y);
            var result = {};
            if(m) {
                result.x /= m;
                result.y /= m;
            }
            return result;
        },

        /**
         * 计算水平轴夹角弧度数
         * @return {Number} 通过反正切计算当前向量与水平轴夹角的弧度数
         */
        angle: function () {
            return Math.atan2(this.y, this.x);
        },

        /**
         * 与指定向量夹角的弧度数
         * @param {Vector} v 同原点的另一个向量
         * @return {Number} 两个同原点的向量夹角的弧度数
         */
        angleTo: function (v) {
            var dx = v.x - this.x,
                dy = v.y - this.y;
            return Math.atan2(dy, dx);
        },

        /**
         * @param {Vector} v 被减向量
         * @param {Number} s 缩放系数
         */
        lerq: function (v, s) {
            this.x += (v.x - this.x) * s;
            this.y += (v.y - this.y) * s;
            return this;
        },

        /**
         * 克隆
         */
        clone: function () {
            return new Vector(this.x, this.y);
        }
    }

    /**
     * 精灵类构造函数
     * @param {String} name 精灵类标识
     * @param {Painter} painter 三种绘制器实例之一
     */
    function Sprite (name, painter) {
        this.name = name || '';
        if(painter !== undefined) this.painter = painter
        else throw new Error('参数 painter 必要');
        Vector.call(this, 0, 0);
        this.width = 10;
        this.height = 10;
        this.speed = new Vector();
        this.visibal = true;
        this.animating = false;
        this.behaviors = [];
    }
    /* --============原型对象============-- */
    Sprite.prototype = (function (o) {
        var v = new Vector(0, 0);
        for(var p in o) {
            v[p] = o[p]
        }
        return v;
    })({
        //策略模式执行绘制函数
        paint: function (ctx) {
            if(this.painter !== undefined && this.visibal) {
                this.painter.paint(this, ctx);
            }
        },
        //执行行为函数
        update: function (ctx, time) {
            for(var i = 0; i < this.behaviors.length; i += 1) {
                this.behaviors[i].execute(this, ctx, time);
            }
        },
        //以上两个 paint就是 我熟悉的代码结构中的 draw 函数，update仍然是update，只不过有多个
        /**
         * 新增行为，接收一个行为函数作为参数
         */
        addBehavior: function (execute, param) {
            if(!execute || typeof execute !== 'function') throw new Error('参数 execute 必要且为函数');
            if(typeof param === 'object' && param.length) throw new Error('参数 param 应为对象字面量');
            var behavior = new Behavior(execute, param);
            this.behaviors.push(behavior);
            return behavior;
        },

        /**
         * 删除行为，接收一个行为对象作为参数
         */
        removeBehavior: function (behavior) {
            for(var i = 0; i < this.behaviors.length; i += 1) {
                if(behavior === this.behaviors[i]) {
                    this.behaviors.splice(i, 1);
                }
            }   
        }
    });

     /* --=================绘制器===============-- */
    /**
     * 描边填充绘制器
     * @param {Function} action 自定义绘制函数
     */
    Sprite.StrokeAndFillPainter = function (action, param) {
        if(typeof action !== 'function') throw new TypeError('参数 action 必要且为函数');
        this.action = action;
    }
    Sprite.StrokeAndFillPainter.prototype = {
        paint: function (sprite, ctx) {
            this.action(sprite, ctx);
        }
    }

    /**
     * 图像绘制器构造函数
     * @param {String} imgUrl 要绘制图像的链接
     */
    Sprite.ImagePainter = function (imgUrl, param) {
        this.image = new Image();
        this.image.src = imgUrl;
        for(var key in param) {
            if(param.hasOwnProperty(key)) {
                this[key] = param[key]
            }
        }
    }
    Sprite.ImagePainter.prototype = {
        paint: function (sprite, ctx) {
            var timer, that = this;
            if(that.image.complete) {
                ctx.drawImage(that.image, sprite.x, sprite.y, sprite.width, sprite.height);
            } else {
                timer = setInterval(function () {
                    if(that.image.complete) {
                        that.paint(sprite, ctx);
                        clearInterval(timer);
                    }
                }, 100);
            }
        }
    }

    /**
     * 精灵表绘制器构造函数
     * @param {Stirng} sheetUrl 精灵图url地址
     * @param {Array(Object)} cells 对象字面量数组，每个对象有x、y、w、h四个属性
     */
    Sprite.SpriteSheetPainter = function (sheetUrl, cells) {
        this.cells = cells || [];
        this.cellIndex = 0;
        this.sheet = new Image();
        this.sheet.src = sheetUrl;
    }
    Sprite.SpriteSheetPainter.prototype = {
        advance: function () {
            if(this.cellIndex === this.cells.length - 1) {
                this.cellIndex = 0;
            } else {
                this.cellIndex++;
            }
        },
        paint: function (sprite, ctx) {
            var timer, that = this;
            if(that.sheet.complete) {
                var cell = that.cells[that.cellIndex];
                ctx.drawImage(that.sheet, cell.x, cell.y, cell.w, cell.h, sprite.left, sprite.top, cell.w, cell.h);
            } else {
                timer = setInterval(function () {
                    if(that.sheet.complete) {
                        that.paint(sprite, ctx);
                        clearInterval(timer);
                    }
                }, 100);   
            }
        }
    }

     /* --=================行为类===============-- */
    /**
     * 行为类构造函数
     * @param {Function} execute 行为最终的执行函数
     * @param {Object} param 对象字面量，给出某个行为独有的辅助变量
     */
    var Behavior = function (execute, param) {
        if(!execute || typeof execute !== 'function') throw new Error('参数 execute 必要且为函数');
        this.action = execute;
        if(param && typeof param === 'object' && !param.length) {
            for(var key in param) {
                if(param.hasOwnProperty(key)) {
                    this[key] = param[key];
                }
            }
        }
    }
    Behavior.prototype = {
        execute: function (sprite, ctx, time) {
            this.action(sprite, this, ctx, time )
        }
    }

    /* --==============精灵绘制器动画制作器===============-- */
    /**
     * 精灵动画制作构造函数，
     * 基于绘制器的动画制作器
     * @param {Array(Painter)} painters Painter数组
     * @param {Function} callback 回调
     */
    Sprite.SpriteAnimator = function (painters, duration, callback) {
        this.painters = painters || [];
        this.callback = callback;
        this.duration = duration || 1000;
        this.startTime = 0;
        this.index = 0;
    }
    Sprite.SpriteAnimator.prototype = {
        end: function (sprite, originalPainter) {
            sprite.animating = false;
            if(this.callback) callback(sprite);
            else sprite.painter = originalPainter;
        },
        start: function (sprite, duration) {
            var endTime = +new Date() + duration,
                period = duration / (this.painters.length),
                animator = this,
                originalPainter = sprite.painter,
                lastUpdate = 0,
                id;
            this.index = 0;
            sprite.animating = true;
            sprite.painter = this.painters[this.index];
            id = requestAnimationFrame( function spriteAnimatorAnimate(time) {
                if(time < endTime) {
                    if((time - lastUpdate) > period) {
                        sprite.painter = animator.painters[++animator.index];
                        lastUpdate = time;
                    }
                } else {
                    animator.end(sprite, originalPainter);
                    cancelAnimationFrame(id);
                }
            });
        }
    }

    /**
     * 秒表类
     */
    function StopWatch () {
        this.startTime = 0;
        this.running = false;
        this.elapsed = undefined;
    };
    //原型
    StopWatch.prototype = {
        //开始计时
        start: function () {
            this.startTime = +new Date();
            this.elapsed = undefined;
            this.running = true;
        },
        //结束计时
        stop: function () {
            this.elapsed = (+new Date()) - this.startTime;
            this.running = false;
        },
        //获取已经过的时间
        getElapsedTime: function () {
            if(this.running) {
                return (+new Date()) - this.startTime;
            } else {
                return this.elapsed;
            }
        },
        //是否正在计时
        isRunning: function () {
            return this.running;
        },
        //重置
        reset: function () {
            this.elapsed = 0;
        }
    }

    /**
     * 动画计时器
     */
    Sprite.AnimationTimer = function (duration, timeWarp) {
        this.duration = duration || 1000;
        if(timeWarp !== undefined) {
            if(typeof timeWarp === 'function') {
                this.timeWarp = timeWarp;
            } else if(typeof timeWarp === 'string') {
                this.timeWarp = Sprite.AnimationTimer.getWrapName(timeWarp);
            } else {
                throw new Error('扭曲函数参数必须为函数 或 指定字符串')
            }
        }
        
        this.stopwatch = new StopWatch();
    }
    //静态方法（扭曲函数）
    Sprite.AnimationTimer.getWrapName = function (str) {
        if(str.indexOf('-') !== -1) {
            var tempArray = str.split('-');
            for(var i = 0; i < tempArray.length; i += 1) {
                tempArray[i] = tempArray[i].replace(/^(\w)/g, RegExp.$1.toUpperCase());
            }
            if(tempArray[2]) {
                return this[tempArray[0]][tempArray[1] + tempArray[2]];
            } else {
                return this[tempArray[0]][tempArray[1]];
            }
        } else {
            return this[str.replace(/^(\w)/g, RegExp.$1.toUpperCase())];
        }
    };
    Sprite.AnimationTimer.Linear = function (p) {
        return p;
    };
    Sprite.AnimationTimer.Ease = {
        In: function (p) {
            return Math.pow(p, 2);
        },
        Out: function (p) {
            return 1 - Math.pow(1 - p, 2);
        },
        InOut: function (p) {
            return p - Math.sin(p * Math.PI * 2) / (Math.PI * 2)
        }
    };
    Sprite.AnimationTimer.Elastic = function (p, press) {
        var press = press || 3; //弹性次数
        return ( (1 - Math.cos(p * Math.PI * press)) * (1 - p) ) + p;
    };
    Sprite.AnimationTimer.Bounce = function (p) {
        var bounces = 4;
        var p = Sprite.AnimationTimer.Elastic(p, bounces);
        return p <= 1 ? p : 2 - p;
    }
    //原型
    Sprite.AnimationTimer.prototype = {
        start: function () {
            this.stopwatch.start();
        },
        stop: function () {
            this.stopwatch.stop();
        },
        getElapsedTime: function () {
            var elapsedTime = this.stopwatch.getElapsedTime(),
                percentComplete = elapsedTime / this.duration; //计算 已经过时间所占百分比
                result = 0;
            if(!this.stopwatch.running) {
                return undefined;
            } else if (this.timeWarp == undefined) {
                return elapsedTime;
            }

            /**
             * timeWarp函数，将给出的 已经过事件所占百分比 通过，自定义的扭曲函数，计算出啊新的  百分比，并除以 原始 百分比，
             * 得到 新elapsedTime，占 原elapsedTime 的百分比
             * 再乘以 原elapsedTime，得到新的扭曲后的已经过时间，并返回
             */
            result = elapsedTime * (this.timeWarp(percentComplete) / percentComplete);
            return result;
        },
        isRunning: function () {
            return this.stopwatch.isRunning();
        },
        isOver: function () {
            return this.stopwatch.getElapsedTime() > this.duration + 10;
        }
    }

    //抛出矢量类
    if(!window.Vector) window.Vector = Vector;

    return Sprite;
})();

} else {
    throw new Error('Sprite被占用');
}
} else {
    throw new Error('环境错误');
}